//------------------------------------------------------------------------------------------------
// The MIT License (MIT)
// 
// Copyright (c) 2019 Crsky
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//------------------------------------------------------------------------------------------------

#include "base_types.h"
#include "high_resolution_timer.h"

#include <windows.h>


//------------------------------------------------------------------------------------------------
// Implementation
//------------------------------------------------------------------------------------------------
class HighResolutionTimer::Impl
{
public:
	Impl();
	Impl( const Impl& ) = delete;
	Impl& operator=( const Impl& ) = delete;
	~Impl();

public:
	void Start();
	void Stop();
	double GetElapsedTimeInSec();
	double GetElapsedTimeInMilliSec();
	double GetElapsedTimeInMicroSec();

private:
	// in micro-second
	double start_time_;
	double end_time_;

	// Tick count
	LARGE_INTEGER frequency_;
	LARGE_INTEGER start_count_;
	LARGE_INTEGER end_count_;

	// Timer is stoped ?
	bool stoped_;
};

//------------------------------------------------------------------------------------------------
// Constructor
//------------------------------------------------------------------------------------------------
HighResolutionTimer::Impl::Impl()
	: start_time_( 0 )
	, end_time_( 0 )
	, frequency_()
	, start_count_()
	, end_count_()
	, stoped_( false )
{
	QueryPerformanceFrequency( &frequency_ );
	start_count_.QuadPart = 0;
	end_count_.QuadPart = 0;
}

//------------------------------------------------------------------------------------------------
// Destructor
//------------------------------------------------------------------------------------------------
HighResolutionTimer::Impl::~Impl()
{
}

//------------------------------------------------------------------------------------------------
// Start timer
//------------------------------------------------------------------------------------------------
void HighResolutionTimer::Impl::Start()
{
	stoped_ = false;
	QueryPerformanceCounter( &start_count_ );
}

//------------------------------------------------------------------------------------------------
// Stop timer
//------------------------------------------------------------------------------------------------
void HighResolutionTimer::Impl::Stop()
{
	stoped_ = true;
	QueryPerformanceCounter( &end_count_ );
}

//------------------------------------------------------------------------------------------------
// Divide ElapsedTimeInMicroSec by 1000000
//------------------------------------------------------------------------------------------------
double HighResolutionTimer::Impl::GetElapsedTimeInSec()
{
	return GetElapsedTimeInMicroSec() * 0.000001;
}

//------------------------------------------------------------------------------------------------
// Divide ElapsedTimeInMicroSec by 1000
//------------------------------------------------------------------------------------------------
double HighResolutionTimer::Impl::GetElapsedTimeInMilliSec()
{
	return GetElapsedTimeInMicroSec() * 0.001;
}

//------------------------------------------------------------------------------------------------
// Compute elapsed time in micro-second resolution
//------------------------------------------------------------------------------------------------
double HighResolutionTimer::Impl::GetElapsedTimeInMicroSec()
{
	if ( !stoped_ )
		QueryPerformanceCounter( &end_count_ );

	start_time_ = start_count_.QuadPart * (1000000.0 / frequency_.QuadPart);
	end_time_ = end_count_.QuadPart * (1000000.0 / frequency_.QuadPart);

	return end_time_ - start_time_;
}

//------------------------------------------------------------------------------------------------
// Pointer to implementation
//------------------------------------------------------------------------------------------------

HighResolutionTimer::HighResolutionTimer()
	: impl_( make_unique<Impl>() )
{
}

HighResolutionTimer::~HighResolutionTimer()
{
}

void HighResolutionTimer::Start()
{
	return impl_->Start();
}

void HighResolutionTimer::Stop()
{
	return impl_->Stop();
}

double HighResolutionTimer::GetElapsedTimeInSec()
{
	return impl_->GetElapsedTimeInSec();
}

double HighResolutionTimer::GetElapsedTimeInMilliSec()
{
	return impl_->GetElapsedTimeInMilliSec();
}

double HighResolutionTimer::GetElapsedTimeInMicroSec()
{
	return impl_->GetElapsedTimeInMicroSec();
}
